package empe;

/**
 * Gere e cria a estrutura de dados que organiza os eventos,
 * por ordem decrescente de tempo.
 * A estrutura consiste numa lista de listas indexada por vector.
 */
public class Cap {
	private Tempo[] tempos;
	private Tempo lista_tempos;



	public Cap() {
		lista_tempos = null;
		tempos = new Tempo[Controlo.ler_tmax()];

	}



	/**
	 * Insere eventos na Cap, por ordem crescente de tempo. 
	 * Verifica se o evento e' valido, ie, ocorre antes da morte, cria um elemento
	 * Tempo caso este ainda nao exista.
	 * Usa as funções insere_lista_evento_individuo() e a insere_evento().
	 * @param novo elemento a inserir na CAP.
	 * @exception NullPointerException termina a execucao com mensagem de erro.
	 */
	protected void insere_elemento(Cap_elemento novo) {

		Tempo temp ,aux;
		Evento nov = (Evento) novo;
		boolean inserido = false;

		try{
			/*apenas insere se o evento nao exceder o tmax e for válido (ser antes da morte)*/
			if(novo.tempo < Controlo.ler_tmax() && (nov.insere_lista_evento_individuo()) == true) {

				if(tempos[novo.tempo] == null) { /*não há entrada no vector*/
					temp = new Tempo(nov.tempo); /* criar o elemento tempo*/
					temp.insere_evento(novo); /*insere o evento*/

					tempos[novo.tempo] = temp; /*insere elemento no vector*/

					/*inserir igualmente na lista*/
					if(lista_tempos == null) {
						lista_tempos = temp; /* os restantes apontadores já se encontram a null (construtor)*/
					}
					else {/*percorre a lista, ficando o ultimo por comparar*/
						for(aux = lista_tempos; aux.proximo != null ;aux = (Tempo) aux.proximo) {
							if(aux.tempo > temp.tempo) { /* encontrado o proximo do novo*/
								temp.proximo = aux;
								temp.anterior = aux.anterior;
								if(aux.anterior != null) /*alteração do primeiro*/
									aux.anterior.proximo = temp;
								else
									lista_tempos = temp; /*de modo a ficar sempre no primeiro*/
								aux.anterior = temp;
								inserido = true;
								break; /*termina o for*/
							}
						}
						if(inserido == false) {/*ainda não inseri o elemento, logo será o último ou o penúltimo*/
							if(aux.tempo > temp.tempo) { /*penultimo*/
								temp.proximo = aux;
								temp.anterior = aux.anterior;
								if(aux.anterior != null)/*alteração do primeiro*/
									aux.anterior.proximo = temp;
								else
									lista_tempos = temp; /*de modo a ficar sempre no primeiro*/
								aux.anterior = temp;	
							}
							else { /* ultimo */
								aux.proximo = temp;
								temp.anterior = aux;
								temp.proximo = null;
							}
						}
					}
				}/*if(tempos[novo.tempo] == null)*/
				else { /* o elemento de tempo já existe*/
					tempos[novo.tempo].insere_evento(novo);
				}

			}
		}catch(NullPointerException e) {
			System.out.println("insere_elemento: Lista_tempos comprometida!!");
			System.exit(1);
		}
	}


	/**
	 * Retira sempre o elemento que se encontra no início da CAP.
	 * Usa as funcoes remove_evento() e ler_evento().
	 * @exception NullPointerException termina a execucao com mensagem de erro.
	 * @return evento que se encontra em primeiro lugar na CAP ou null se a CAP 
	 * estiver vazia.
	 */
	protected Evento retira_prox_elemento() {

		Cap_elemento evento;
		try {
			if(lista_tempos == null)/*não há elementos*/
				return null;
			else {
				evento = lista_tempos.ler_evento(); /*guarda o evento*/
				/*retira-o da lista*/
				if(lista_tempos.remove_evento(evento) == true) { /*não existem mais eventos neste instante de tempo*/

					tempos[lista_tempos.tempo] = null;
					lista_tempos = (Tempo) lista_tempos.proximo;
					if(lista_tempos != null) 
						lista_tempos.anterior = null;


				}

			}

			return  (Evento) evento;

		}catch(NullPointerException e) {
			System.out.println("retira_elemento: Lista_tempos comprometida!!");
			System.exit(1);
			return null;
		}
	}


	/**
	 * Retira um qualquer evento cuja referencia seja passada como argumento,
	 * util no caso de epidemias, caso o evento retirado seja o ultimo do elemento Tempo
	 * este e' eliminado e todas as referencias actualizadas.
	 * Usa a funcao remove_evento().
	 * @param ev referencia do evento a retirar.
	 * @exception NullPointerException termina a execucao com mensagem de erro.
	 */
	protected void retira_elemento(Cap_elemento ev) {


		try {
			if(tempos[ev.tempo] != null) {
				if((tempos[ev.tempo].remove_evento(ev)) == true) { /*não existem mais eventos neste instante de tempo*/


					if(tempos[ev.tempo].tempo == lista_tempos.tempo) { /* e' o primeiro da lista, que se vai eliminar*/
						lista_tempos = (Tempo) lista_tempos.proximo;
						if(lista_tempos != null)
							lista_tempos.anterior = null;
					}
					else {
						if(tempos[ev.tempo].proximo != null)
							tempos[ev.tempo].proximo.anterior = tempos[ev.tempo].anterior;

						tempos[ev.tempo].anterior.proximo = tempos[ev.tempo].proximo;

						tempos[ev.tempo].anterior = tempos[ev.tempo].proximo = null;
					}

					tempos[ev.tempo] = null;	
				}
			}
		}catch(NullPointerException e) {
			System.out.println("retira_elemento: Lista_tempos comprometida!!");
			System.exit(1);
		}
	}


	/**
	 * Devolve o tempo do primeiro evento da CAP, nao retirando o evento.
	 * Util para uma correcta escrita das estatisticas durante a simulacao.
	 * Usa a funcao ler_evento().
	 * @return tempo do primeiro evento da CAP.
	 */
	protected int tempo_prox_evento () {

		Cap_elemento evento;
		try{
			if(lista_tempos == null)/*não há elementos*/
				return Controlo.ler_tmax();
			else {
				evento = lista_tempos.ler_evento(); /*guarda o evento*/
				return evento.tempo;

			}
		}catch(NullPointerException e) {
			System.out.println("tempo_prox_evento: Lista_tempos comprometida!!");
			System.exit(1);
			return -1;
		}
	}


}
